#include "EglWrapper.h"
#include "EngineWindow.h"
#include "ExtException.h"
#include "eerrors.h"
#include "OGLDefs.h"
#include "CoreGraphics/CGDirectDisplay.h"

////////////////////////////////////////////////
/// EglWrapper
IEglWrapper::IEglWrapper() 
	: m_display(0)
	, m_surface(0)
	, m_config(0) {
}
#if defined _WIN32 || defined WIN32
////////////////////////////////////////////////
/// EglWrapperWin
class EglWrapperWin : public IEglWrapper {
public:
	EglWrapperWin(const EglSetup& setup, IEngineWindowPtr window);
	virtual ~EglWrapperWin();
	virtual EGL_CONTEXT CreateContext();
	virtual EGL_CONTEXT GetCurrentContext();
	virtual void DeleteContext(EGL_CONTEXT context);
	virtual void MakeCurrent(EGL_CONTEXT context);
	virtual void Present();
private:
};

EglWrapperWin::EglWrapperWin(const EglSetup& setup, IEngineWindowPtr window) {
    PIXELFORMATDESCRIPTOR pfd;
    int iFormat;

    m_display = GetDC( window->GetWindowHandle() );
    if (!m_display) {
    	EXT_EXCEPTION(EGL_WRAPPER_ERROR_DISPLAY);
    }

    ZeroMemory( &pfd, sizeof( pfd ) );
    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
                  PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = setup.RedBits + setup.GreenBits + setup.BlueBits;
    pfd.cDepthBits = setup.DepthBits;
    pfd.cStencilBits = setup.StencilBits;
    pfd.iLayerType = PFD_MAIN_PLANE;
    iFormat = ChoosePixelFormat( m_display, &pfd );
    SetPixelFormat( m_display, iFormat, &pfd );	
}

EglWrapperWin::~EglWrapperWin() {
}

EGL_CONTEXT EglWrapperWin::CreateContext() {
	EGL_CONTEXT context = wglCreateContext(m_display);
	if (!context) {
		EXT_EXCEPTION(EGL_WRAPPER_ERROR_CREATE_CTX);
	}
    return context;
}

EGL_CONTEXT EglWrapperWin::GetCurrentContext() {
	return wglGetCurrentContext();
}

void EglWrapperWin::DeleteContext(EGL_CONTEXT context) {
	wglDeleteContext(context);
}

void EglWrapperWin::MakeCurrent(EGL_CONTEXT context) {
	if (!wglMakeCurrent(m_display, context)) {
		EXT_EXCEPTION(EGL_WRAPPER_ERROR_MAKE_CURRENT_CTX);
	}
}

void EglWrapperWin::Present() {
	SwapBuffers(m_display);
}
#endif // WIN32
#if defined X11 || defined __APPLE__
////////////////////////////////////////////////
/// EglWrapperMac
class EglWrapperMac : public IEglWrapper {
public:
	EglWrapperMac(const EglSetup& setup, IEngineWindowPtr window);
	virtual ~EglWrapperMac();
	virtual EGL_CONTEXT CreateContext();
	virtual EGL_CONTEXT GetCurrentContext();
	virtual void DeleteContext(EGL_CONTEXT context);
	virtual void MakeCurrent(EGL_CONTEXT context);
	virtual void Present();
private:
};

EglWrapperMac::EglWrapperMac(const EglSetup& setup, IEngineWindowPtr window) {
    CGDirectDisplayID directDisplayId = CGMainDisplayID();
    CGOpenGLDisplayMask displayMask = CGDisplayIDToOpenGLDisplayMask(directDisplayId);
	vector<int32_t> attributes;
    attributes.push_back(kCGLPFADisplayMask),   attributes.push_back(displayMask);
	attributes.push_back(kCGLPFADoubleBuffer);
	attributes.push_back(kCGLPFAColorSize), 	attributes.push_back(setup.RedBits+setup.GreenBits+setup.BlueBits);
	attributes.push_back(kCGLPFAAlphaSize), 	attributes.push_back(setup.AlphaBits);
	attributes.push_back(kCGLPFADepthSize), 	attributes.push_back(setup.DepthBits);
	attributes.push_back(kCGLPFAStencilSize), 	attributes.push_back(setup.StencilBits);
	attributes.push_back(0);
	GLint numDisplays;
	if (kCGLNoError != CGLChoosePixelFormat((CGLPixelFormatAttribute*)attributes.data(), &m_display, &numDisplays) || !numDisplays) {
		//throw EXT_EXCEPTION(EGL_WRAPPER_ERROR_DISPLAY);
	}
}

EglWrapperMac::~EglWrapperMac() {
    CGLDestroyPixelFormat(m_display);
}

EGL_CONTEXT EglWrapperMac::CreateContext() {
	EGL_CONTEXT context;
	if (kCGLNoError != CGLCreateContext(m_display, NULL, &context)) {
		//throw EXT_EXCEPTION(EGL_WRAPPER_ERROR_CREATE_CTX);
	}
	return context;
}

EGL_CONTEXT EglWrapperMac::GetCurrentContext() {
	return CGLGetCurrentContext();
}

void EglWrapperMac::DeleteContext(EGL_CONTEXT context) {
	CGLReleaseContext(context);
}

void EglWrapperMac::MakeCurrent(EGL_CONTEXT context) {
	if (kCGLNoError != CGLSetCurrentContext(context)) {
		//throw EXT_EXCEPTION(EGL_WRAPPER_ERROR_MAKE_CURRENT_CTX);
	}
}

void EglWrapperMac::Present() {
	CGLFlushDrawable(GetCurrentContext());
}
#endif // __APPLE__
#if defined __ANDROID__
////////////////////////////////////////////////
/// EglWrapperAndroid
class EglWrapperAndroid : public IEglWrapper {
public:
	EglWrapperAndroid(IEngineWindowPtr window);
	virtual ~EglWrapperAndroid();
	virtual EGL_CONTEXT CreateContext();
	virtual EGL_CONTEXT GetCurrentContext();
	virtual void DeleteContext(EGL_CONTEXT context);
	virtual void MakeCurrent(EGL_CONTEXT context);
	virtual void SwapBuffers();
private:
};

EglWrapperAndroid::EglWrapperAndroid(const EglSetup& setup, IEngineWindowPtr window) {
    EGLint format;
    EGLint numConfigs;
    // Create attriblist
    vector<EGLint> configAttribs;
    configAttribs.push_back(EGL_COLOR_BUFFER_TYPE); configAttribs.push_back(EGL_RGB_BUFFER); 
    configAttribs.push_back(EGL_RED_SIZE);			configAttribs.push_back(setup.RedBits);
	configAttribs.push_back(EGL_GREEN_SIZE);		configAttribs.push_back(setup.GreenBits);
	configAttribs.push_back(EGL_BLUE_SIZE);			configAttribs.push_back(seyup.BlueBits);
    configAttribs.push_back(EGL_ALPHA_SIZE);		configAttribs.push_back(setup.AlphaBits);
    configAttribs.push_back(EGL_DEPTH_SIZE);		configAttribs.push_back(setup.DepthBits);
    configAttribs.push_back(EGL_STENCIL_SIZE);		configAttribs.push_back(setup.DepthBits);


    m_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (!m_display) {
    	EXT_EXCEPTION(EGL_WRAPPER_ERROR_DISPLAY);
    }
    if (eglInitialize(m_display, 0, 0) != EGL_TRUE) {
        EXT_EXCEPTION(EGL_WRAPPER_ERROR_INIT_EGL);
    }
    if (eglChooseConfig(m_display, configAttribs.data(), &m_config, 1, &numConfigs) != EGL_TRUE) {
        EXT_EXCEPTION(EGL_WRAPPER_ERROR_CHOOSE_CONFIG);
    }

    m_surface = eglCreateWindowSurface(m_display, m_config, window->GetWindowHandle(), NULL);
    if (EGL_NO_SURFACE == m_surface) {
        EXT_EXCEPTION(EGL_WRAPPER_ERROR_SURFACE);
    }    
}

EglWrapperAndroid::~EglWrapperAndroid() {
}

EGL_CONTEXT EglWrapperAndroid::CreateContext() {
    EGL_CONTEXT context = eglCreateContext(m_display, m_config, NULL, in_pContextAttribs);
    if (EGL_NO_CONTEXT == context) {
        EXT_EXCEPTION(EGL_WRAPPER_ERROR_CREATE_CTX);
    }
}

EGL_CONTEXT EglWrapperAndroid::GetCurentContext() {
	return eglGetCurrentContext();
}

virtual void EglWrapperAndroid::DeleteContext(EGL_CONTEXT context) {
	eglDestroyContext(context);
}

virtual void EglWrapperAndroid::MakeCurrent(EGL_CONTEXT context) {
    if (eglMakeCurrent(m_display, m_surface, m_surface, context) == EGL_FALSE) {
        EXT_EXCEPTION(EGL_WRAPPER_ERROR_MAKE_CURRENT_CTX);
    }}

virtual void EglWrapperAndroid::SwapBuffers() {
	eglSwapBuffers(m_display, m_surface);
}
#endif //__ANDROID__

EglWrapperPtr CreateEglWrapper(const EglSetup& setup, IEngineWindowPtr window) {
#if defined _WIN32 || defined _WIN32
	return make_shared<EglWrapperWin>(setup, window);
#elif defined X11 || defined __APPLE__
	return make_shared<EglWrapperMac>(setup, window);
#elif define __ANDROID__
	return make_shared<EglWrapperAndroid>(setup, window);
#else
	#error Unsupported platform
#endif
}
